﻿using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using Alipay.AopSdk.AspnetCore;
using Autofac;
using Casamiel.API.Application.Commands;
using Casamiel.API.Infrastructure;
using Casamiel.API.Infrastructure.AutofaceModules;
using Casamiel.API.Infrastructure.Filters;
using Casamiel.API.Infrastructure.Middlewares;
using Casamiel.Application.Commands;
using Casamiel.Common;
using Casamiel.Domain.Entity;
using FluentValidation;
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using NLog;
using Polly;
using Swashbuckle.AspNetCore.Filters;
using static Casamiel.Common.IcServiceSettings;

namespace Casamiel.API
{
	/// <summary>
	/// Startup.
	/// </summary>
	public class Startup
    {
        private readonly NLog.ILogger _logger = LogManager.GetLogger("ModelStateService");

        /// <summary>
        /// 
        /// </summary>
        /// <param name="configuration"></param>
 
        public Startup(IConfiguration configuration) 
        {
            Configuration = configuration;
            
        }
        
        /// <summary>
        /// Gets the configuration.
        /// </summary>
        /// <value>The configuration.</value>
        public IConfiguration Configuration { get; }

        /// <summary>
        /// Configures the auth.
        /// </summary>
        /// <param name="app">App.</param>
        protected virtual void ConfigureAuth(IApplicationBuilder app)
        {
            if (Configuration.GetValue<bool>("UseLoadTest")) {
                app.UseMiddleware<ByPassAuthMiddleware>();
            }

            app.UseAuthentication();
        }
        /// <summary>
        /// Gets the application container.
        /// </summary>
        /// <value>The application container.</value>
        public IContainer ApplicationContainer { get; private set; }
        /// <summary>
        /// Configures the services.
        /// </summary>
        /// <returns>The services.</returns>
        /// <param name="services">Services.</param>
        public void ConfigureServices(IServiceCollection services)
        { 
            services.Configure<IISServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });
            services.AddMediatR(typeof(BindMemberCardCommandHandler).GetTypeInfo().Assembly);
            services.AddMediatR(typeof(CreateICConsumeCodeCommand).GetTypeInfo().Assembly);
            services.AddEnyimMemcached(options => Configuration.GetSection("enyimMemcached").Bind(options));
            // 选项确定路由是应在内部使用 ASP.NET Core 2.1 或更早版本的基于终结点的逻辑还是使用其基于 IRouter 的逻辑
            //services.AddMvc(options => options.EnableEndpointRouting = true)
            //    .SetCompatibilityVersion(CompatibilityVersion.Latest);

            services.AddAlipay(options => {
                options.AlipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkqnM+GQqaoeMKoTjcFzgGxcS3HJGTKPaOmsjU3BxZ4qUzIWJmy298Qgg9OXk8fF3jKJQ/ViStpfTgXOexgszRM8XnVDzzN483yaJ2EHuh+8EmJZmQyLo4c9oc5hMyZcnE3B7FznY2+8LQpaYipLyRcMLbCLb7pYROntPCDDzV4VksapSl/qIJV0asAUHeOxrtJZwfh86d6ESuPuP5N6OoZJalnNtPQiZCmaIL/0oirjtMcT2/zGlwmzFv4f+4HlRRGBNr8LBc9AnKhPoyeF2OAxZsTk8CxOiJtZZftNxhVTYQArODYQCI5SvYb+HrTNRlENhaAtzFJAzJznMCg3m+wIDAQAB";
                options.AppId = "2019062465581893";
                options.CharSet = "UTF-8";
                options.Gatewayurl = "https://openapi.alipay.com/gateway.do";
                options.PrivateKey = "MIIEpgIBAAKCAQEA2aWcg/he4a/zkrhClTRUzIjyO7X7PBlxRWLArMfZJdM9+gr4z/5+2PpmocZjFWH9Njm0TiR+GThwNBhGmBB7TpoS5KxKhBLy1JPUn8JYICxwZb2Pay6HqCdu2nfsWdo9T+uINwLsPiEKB0yWJJGMx5qGIGH83H/oMQZR6YJ1tz6lNE0WFbpxA9kn9aVlwqHo+vOOzAaQu1Bf3f4Ovie8e5BAIx9cZBshOPPHm+fI4/tSAlK6WtnIim0MdcN2tBo+WaMdHUQP/Yurt+1JMxMFb8T4MXx3SchOOWEWRSnJKSSsT4t9L3B9a50PP+gUYPaX/FLDRp7EXAchqSBNCcsL+QIDAQABAoIBAQDMpFgvCfiEsgEOwmvwiJz3GAjTNf/8+Vs0x26sxtgZJa9PjMN4Ji207HRVxghiEZmZQ9aR5uQd6KAvi2fNESovtNTw6ELEPmDOmhVBBeYAkmg4cW0JdV2eMT2c80drELW3kJmqR/hDTqN0S4Gc7rH0+AkHJ7suxKFYfz3DlqQCX4t6xUs1GQdCE6iAntVHRW+ozWET5CoLBY5t5de8mrxY3QXziJBuQOcOZYMInebNyd68YfOkpF7SUNXXE2KewEcFJEUgw6n8UTijxnFTGo/1FklOhV0o3+wcskGTD1qzQ3BsATN9GVqsVnXTmu/XFC8xdN1x/oxJbYLqFvYd2+mRAoGBAPamrrfL4/PLqxnTDulSwmusP1e3oJX1uBGVh9LyVuW9Q5YO+qtkZr1bBTu2stTStbUfrvyZQn+x/YZEQauRdx7dRgRIsni7OUVOLtKcDFpTil0grE84ySjMEiI01Vq+NTTLupZEN9sgwxfm2dRL1cWYFhcMhCRrn2daG1MjcKV9AoGBAOHlfniAUWrMXEIjmrHncEnHcaY1of2VaM5sq7Pchd5EyPpjyBS/VrXrdu8+aTbfOIHxWxv41iOdYcKKfWDsMZ/+M2brxaWZqNh27xg1FwyO2CxYySlqZCxzFe/XcYraKl2Qg3YhB6/7habCj//jZCwboCv1p9p2bYjDviV/Q9ktAoGBAL17oZ/LP+yr2180eDEhBgFHy0Ws6O4rXeQMVtLkn4NQ2dou2b3PnOn9QdD08v9yfGuP0pN1fdOxvk1mpHiUgMmyPbbOzmTXmBC5FllvzkGaCiKvFhCqVEdGNuwWYI2csC1GEaqc77gkvBvhmhhwxeJNizLbx6xfDY+Ndo5xHHnVAoGBAINIpR/TM34mFtg5aQZLw26dgudd9C80Thzp+0AkwYhT4Znjs7ybFwHTPuP8GZnSUTzz0EeJbqp0JQSzB/r0SkL5sVX3YH7FtuNW+83NzP93ZRsg3og7VMygdfdV9p8D++s0ubJFr/oO67XnDVliBi28QHJL6qmEelQprrCMLhphAoGBAMA6m3+AHqOdlv3jadFZvkbsL3AljDfX4zZbyKU1Hqdkss2jmQSZ3PVMonHDf8VvSz+PWD0yLpcK7u3ONjGciJdlWN7bW7gU3gS9T5ZtQAHTCCY8enpc0V/XijaPH43G4f0b4nO4l7g4SK6G7/6JODVpVXhiX4gnAL8ySmj1nRsS";
                options.SignType = "RSA2";
                options.Uid = "2088411863302578";
            });

           
            services.AddApiVersioning(options => options.ReportApiVersions = true);
            services.AddVersionedApiExplorer(
               options => {
                   options.GroupNameFormat = "'v'VVV";

                   // note: this option is only necessary when versioning by url segment. the SubstitutionFormat
                   // can also be used to control the format of the API version in route templates
                   options.SubstituteApiVersionInUrl = true;
               });
            services.AddMemoryCache();
            services.AddSingleton<IMemoryCache>(factory => {
                var cache = new MemoryCache(new MemoryCacheOptions());
                return cache;
            });
            //var fallbackResponse = new HttpResponseMessage {
            //    Content = new StringContent("{\"code\":9999, \"msg\":\"fallback\"}"),
            //    StatusCode = System.Net.HttpStatusCode.TooManyRequests
            //};
            services.AddHttpClient("weixin", c => {
                c.BaseAddress = new Uri("https://api.weixin.qq.com/");
                c.DefaultRequestHeaders.Add("Accept", "application/json"); // Github API versioning
            }).AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(5, _ => TimeSpan.FromMilliseconds(100)))
                    .AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(2)))
                    .SetHandlerLifetime(TimeSpan.FromMinutes(5));
            var appurlist = Configuration.GetSection("ICServiceSettings:appUrls").Get<List<AppUrl>>();
            services.AddHttpClient("icapi", c => {
                c.BaseAddress = new Uri(appurlist[0].Url);
                c.DefaultRequestHeaders.Add("Accept", "application/xml, text/xml");
                c.Timeout = TimeSpan.FromSeconds(120);
                c.DefaultRequestHeaders.Add("User-Agent", "casamiel.api");
            }).AddTransientHttpErrorPolicy(p => p.RetryAsync(5))
                  
                    .SetHandlerLifetime(TimeSpan.FromMinutes(5));
            services.AddHttpClient("icapi1", c => {
                //c.BaseAddress = new Uri(Configuration.GetSection("ICServiceSettings")["appUrl"]);
                c.BaseAddress = new Uri(appurlist[1].Url);
                c.DefaultRequestHeaders.Add("Accept", "application/xml, text/xml");
                c.Timeout = TimeSpan.FromSeconds(120);
                c.DefaultRequestHeaders.Add("User-Agent", "casamiel.api");
            }).AddTransientHttpErrorPolicy(p => p.RetryAsync(5))
                  
                  .SetHandlerLifetime(TimeSpan.FromMinutes(5));
            services.AddHttpClient("storeapi", c => {
                c.BaseAddress = new Uri(Configuration.GetValue<string>("StoreMApiUrl"));
                c.DefaultRequestHeaders.Add("Accept", "application/json");
                c.DefaultRequestHeaders.Add("User-Agent", "casamiel.api");

            }).AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(100)))
                    .AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(3)))
                    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

            services.AddHttpClient("mallapi", c => {
                c.BaseAddress = new Uri(Configuration.GetValue<string>("PsStoreApiUrl"));
                c.DefaultRequestHeaders.Add("Accept", "application/json");
                c.DefaultRequestHeaders.Add("User-Agent", "casamiel.api");
            }).AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(100)))
                   .AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(3)))
                   .SetHandlerLifetime(TimeSpan.FromMinutes(5));
            services.AddHttpClient("newstoreApi", c => {
                c.BaseAddress = new Uri(Configuration.GetValue<string>("NewStoreApiUrl"));
                c.DefaultRequestHeaders.Add("Accept", "application/json");
                c.DefaultRequestHeaders.Add("User-Agent", "casamiel.api");

            }).AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(100)))
                  .AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(3)))
                  .SetHandlerLifetime(TimeSpan.FromMinutes(5));

            services.AddHttpClient("JdOpenApi", c => {
                c.BaseAddress = new Uri(Configuration.GetValue<string>("JdopenUrl"));
                c.DefaultRequestHeaders.Add("Accept", "application/json");
                c.DefaultRequestHeaders.Add("User-Agent", "casamiel.api");

            }).AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(3)))
                    .AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(3)))
                    .SetHandlerLifetime(TimeSpan.FromMinutes(5));
            services.AddHttpClient("InvoiceApi", c => {
                c.BaseAddress = new Uri(Configuration.GetValue<string>("InvoiceApiUrl"));
                c.DefaultRequestHeaders.Add("Accept", "application/json");
                c.DefaultRequestHeaders.Add("User-Agent", "casamiel.api");

            }).AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(100)))
                   .AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(3)))
                   .SetHandlerLifetime(TimeSpan.FromMinutes(5));

            services.AddHttpClient("MentuanOpen", c => {
                c.BaseAddress = new Uri(Configuration.GetValue<string>("MentuanOpenUrl"));
                c.DefaultRequestHeaders.Add("Accept", "application/json");
                c.DefaultRequestHeaders.Add("User-Agent", "casamiel.api");

            })
               
                .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>().CircuitBreakerAsync(3, TimeSpan.FromSeconds(1), (ex, ts) => {
                    _logger.Error($"break here {ts.TotalMilliseconds}");
                }, () => {
                    _logger.Error($"reset here ");
                })).AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(120))
                   // .AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(100)))
                   //.AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(3)))
                   .SetHandlerLifetime(TimeSpan.FromMinutes(5));
            services.Configure<Microsoft.AspNetCore.Mvc.ApiBehaviorOptions>(options => {
                //options.SuppressModelStateInvalidFilter = true;
                // options.SuppressConsumesConstraintForFormFileParameters = true;
                options.InvalidModelStateResponseFactory = actionContext => {
                    var errors = actionContext.ModelState
                        .Where(e => e.Value.Errors.Count > 0)
                         .Select(e => e.Key + ":" + e.Value.Errors.First().ErrorMessage
                        ).ToArray();
                    var errorsc = actionContext.ModelState
                        .Where(e => e.Value.Errors.Count > 0).Select(e => new {
                            Name = e.Key,
                            Message = e.Value.Errors.First().ErrorMessage
                        })
                                              .ToArray();
                    var json = new {
                        code = 9999,
                        content = errorsc,
                        msg = string.Join(',', errors)
                    };
                    _logger.Error($"{actionContext.HttpContext.Request.GetShortUri()},{JsonConvert.SerializeObject(json)}");
                    return new JsonResult(json);
                };

            });

            services.AddSingleton<ICacheService, MemoryCacheService>();

            var identityUrl = Configuration["IdentityUrl"];// Configuration.GetValue<string>("IdentityUrl");
            // services.AddMvcCore()
            services.AddControllersWithViews(options => {
                options.EnableEndpointRouting = false;
                options.Filters.Add(typeof(HttpGlobalExceptionFilter));
                options.Filters.Add(typeof(ValidateModelStateFilter));
                options.Filters.Add<ActionFilter>();
              }).AddControllersAsServices()
              .AddNewtonsoftJson();

            services.AddCors(options =>
            {
                options.AddPolicy("any", builder =>
                {

                    builder.WithOrigins(Configuration["AllowedOrigins"].Split(','))
                   .AllowAnyMethod()
                   .AllowAnyHeader()
                   .SetIsOriginAllowedToAllowWildcardSubdomains()
                   .AllowCredentials();
                   
                });
                //builder1.WithOrigins("http://localhost:6621", "http://localhost:3789", "https://store.casamiel.cn", "https://api.casamiel.cn") //允许任何来源的主机访问
                //    .AllowAnyMethod()
                //    .AllowAnyHeader()
                //    .AllowCredentials();//指定处理cookie
                //});
            });

            services.AddMvcCore().AddAuthorization();
            // .AddJsonFormatters();
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = identityUrl;
                options.RequireHttpsMetadata = false;
                options.ApiName = Configuration["ApiName"];// "casamielnetwork";
            });

           
             
            services.Configure<CasamielSettings>(Configuration);
            services.Configure<AppRegCard>(Configuration.GetSection("AppRegCard"));
            #region ActivityInfo
            services.Configure<List<ActivityInfo>>(Configuration.GetSection("ActivityInfos"));
            #endregion
            #region InvoiceSettings
            services.Configure<InvoiceSettings>(Configuration.GetSection("InvoiceSettings"));

            #endregion
            #region 全国配
            services.Configure<MallSettings>(Configuration.GetSection("MallSettings"));
            #endregion

            #region icservice配置
            services.Configure<IcServiceSettings>(Configuration.GetSection("IcServiceSettings"));
            #endregion

            #region 微信支付 app支付
            services.Configure<WxPayAppSettings>(Configuration.GetSection("WxPayAppSettings"));
            #endregion
            #region 微信小程序
            services.Configure<List<Casamiel.Common.MiniAppSettings>>(Configuration.GetSection("MiniAppPaySettings"));
            #endregion

            #region 微信支付-默认
            services.Configure<WxPaySettigs>(Configuration.GetSection("WxPaySettigs"));
            #endregion

            #region 阿里云配置
            services.Configure<AliyunSettings>(Configuration.GetSection("AliyunSettings"));
            #endregion

            #region 达达配置
            services.Configure<DaDaSettings>(Configuration.GetSection("DaDaSettings"));
            #endregion
            #region 高德配置
            services.Configure<AMapConfig>(Configuration.GetSection("AMapConfig"));
            #endregion

            #region swagger
            services.AddSwaggerGen(c =>
            {
                var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
                
                foreach (var description in provider.ApiVersionDescriptions)
                {
                    c.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
                }
                c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Casamiel.API.xml"));
                c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Casamiel.Domain.xml"));
                

                c.DocumentFilter<TagDescriptionsDocumentFilter>();
                
                c.OperationFilter<SecurityRequirementsOperationFilter>();
                c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme()
                {
                    Description = "权限认证(数据将在请求头中进行传输) 参数结构: \"Authorization: Bearer {token}\"",
                    Type = SecuritySchemeType.ApiKey,
                    In = ParameterLocation.Header,
                    Name = "Authorization"
                });
            });

            #endregion



            var builder = new ContainerBuilder();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddOptions();
            
        }
       
        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterModule(new ApplicationModule(Configuration["ConnectionString"]));
             
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="app"></param>
        /// <param name="provider"></param>
        public void Configure(IApplicationBuilder app, IApiVersionDescriptionProvider provider)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);//这是为了防止中文乱码
                                                                          //loggerFactory.AddNLog();//添加NLog

             
            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });

            app.UseSwagger(c => {
                c.PreSerializeFilters.Add((doc, _) => {
                    doc.Servers?.Clear();
                });
            }
              );

            app.UseSwaggerUI(
               options =>
               {
                   // build a swagger endpoint for each discovered API version
                   foreach (var description in provider.ApiVersionDescriptions)
                   {
                       options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
                   }
               });
 
            app.UseEnyimMemcached();
            app.UseResponseTime();

            app.UsePaySharp();
            app.UseAuthentication();
            //小程序支付回调
            app.UseRewriter(new Microsoft.AspNetCore.Rewrite.RewriteOptions().AddRewrite("MiniAppPayNotify/(.*)", "MiniAppPayNotify?$1", true));


            //app.UseMvcWithDefaultRoute();

            var defaultFilesOptions = new DefaultFilesOptions();
            defaultFilesOptions.DefaultFileNames.Add("index.html");    //将index.html改为需要默认起始页的文件名.
            app.UseDefaultFiles(defaultFilesOptions);
            app.UseStaticFiles();

            


            app.UseRouting();
            app.UseCors("any");
            //启用 Authentication 
            app.UseAuthorization();
           

            
            // 短路中间件，配置Controller路由
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
 
        }

       




        /// <summary>
        /// Creates the info for API version.
        /// </summary>
        /// <returns>The info for API version.</returns>
        /// <param name="description">Description.</param>
        static Microsoft.OpenApi.Models.OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
        {
            var info = new OpenApiInfo()
            {
                Title = $"Casa API {description.ApiVersion}",
                Version = description.ApiVersion.ToString(),
                Description = "casamiel api with Swagger, Swashbuckle, and API versioning.",
                Contact = new OpenApiContact() { Name = "shifuwei", Email = "stonesh@qq.com" }

            };

            if (description.IsDeprecated)
            {
                info.Description += " This API version has been deprecated.";
            }
            return info;
        }
    }
}
